package jflowmap.model.ontology;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import jflowmap.model.graph.Graph;
import jflowmap.views.AbstractView;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

public class View {

	private final Context context;
	private final Map<String, Set<Dimension>> dimensions = Maps.newHashMap();
	private final static Map<Context, Map<String, Selection>> selections = Maps.newHashMap();
	
	private AbstractView abstractView;
	
	View (final Context context) {
		this.context = context;
		
		if (!selections.containsKey(context)) {
			selections.put(context, Maps.<String, Selection>newHashMap());
		}
	}
		
	public void setAbstractView (final AbstractView abstractView) {
		this.abstractView = abstractView;
	}
	
	public void setDimensions (final String name, final DimensionCollection dimensions) {
		if (!this.dimensions.containsKey(name)) {
			this.dimensions.put(name, new HashSet<Dimension>());
		}		
		this.dimensions.get(name).addAll(dimensions.get());
	}
	
	public void constrainDimensions(String identifier, Predicate<Dimension> predicate) {
		final Set<Dimension> dimensions = this.require(identifier);
		final Set<Dimension> constrainedDimensions = Sets.newHashSet();
		
		for (Dimension d: dimensions) {
			if (predicate.apply(d)) {
				constrainedDimensions.add(d);
			}
		}
		this.dimensions.put(identifier, constrainedDimensions);
	}
	
	public void constrainDimensions(String identifier, Function<Set<Dimension>, Set<Dimension>> filter) {
		this.dimensions.put(identifier, filter.apply(this.require(identifier)));
	}
	
	public void constrainForView () {
		abstractView.constrain(dimensions);
	}
	
	public Set<Dimension> require (String dimensionIdentifier) {
		return Collections.unmodifiableSet(dimensions.get(dimensionIdentifier));
	}
	
	public Collection<String> getDimensionIdentifiers() {
		return Collections.unmodifiableSet(this.dimensions.keySet());
	}
	
	public Selection getSelection (String name) {
		if (!selections.get(context).containsKey(name)) {
			final Selection newSelection = new Selection(this);
			selections.get(context).put(name, newSelection);
			return newSelection;
			
		} else {
			return selections.get(context).get(name);
		}
	}
	
	public Graph getGraph (String graphName, Set<Dimension> sourceDimensions, 
			Set<Dimension> valueDimensions, Set<Dimension> targetDimensions) {
		
		if (!context.existsGraph(graphName)) {
			context.defineGraph(graphName).dimension(sourceDimensions).asSource()
			.dimension(valueDimensions).asValues()
			.dimension(targetDimensions).asTarget();
		}
		
		return context.getGraph(graphName);
	}

	public Context getContext() {
		return context;
	}

	public void update() {
		this.abstractView.update();
	}

	public SelectionDefinition chooseSelection(
			Collection<SelectionDefinition> near, Set<Dimension> dimSet) {
		
		System.out.println("Choosing one of several selection definitions: " +  near.iterator().next().getName());
		return abstractView.chooseSelection(near, dimSet);
	}

	public SelectionDefinition autocompleteSelection(SelectionDefinition next,
			Set<Dimension> dimSet) {
		
		System.out.println("Selecting one of one exact selection definition: " + next.getName());
		return abstractView.autocompleteSelection(next, dimSet);
	}
}
